package com.itheima.config;

import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

@Configuration
public class RabbitMQConfig {
    @Resource
    private RabbitTemplate rabbitTemplate;

    @PostConstruct
    public void initRabbitTemplate(){
        // 设置消息抵达消息代理的回调
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback(){
            /**
             * @param correlationData   消息的唯一id
             *                          private volatile String id;// 消息唯一ID
             *                          private volatile ReturnedMessage returnedMessage; // 如果消息被退回，这里有退回的详细信息
             * @param ack  消息已经到达 Broker，被 Broker 接收
             * @param cause  原因
             */
            @Override
            public void confirm(CorrelationData correlationData, boolean ack, String cause) {
                System.out.println("confirm...correlationData["+ correlationData+"] === >ack["+ack+"] ===> cause["+cause+ "]");
            }
        });
        //设置消息抵达队列的失败回调
        rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback(){
            /**
             * 只要消息没有投递给指定的队列，就触发这个失败回调
             * @param returnedMessage  ReturnedMessage包含以下数据
             *     private final Message message;  投递失败的消息详细信息
             *     private final int replyCode;    回复的状态码
             *     private final String replyText; 回复的文本内容
             *     private final String exchange;  当时这个消息发给哪个交换机
             *     private final String routingKey; 当时这个消息用哪个路由键
             */
            @Override
            public void returnedMessage(ReturnedMessage returnedMessage) {
                System.out.println("message = " + returnedMessage.getMessage() +
                        " replyCode = " + returnedMessage.getReplyCode() + "replyText = " + returnedMessage.getReplyText()
                        + " exchange = " + returnedMessage.getExchange() + "routingKey = " + returnedMessage.getRoutingKey());
            }
        });
    }

    // ==================== 流程1：秒杀订单创建（普通队列）====================

    /**
     * 秒杀业务交换机
     */
    @Bean
    public DirectExchange seckillExchange() {
        return new DirectExchange("exchange.direct", true, false);
    }

    /**
     * 秒杀订单队列（普通队列，立即处理）
     * 用于创建订单
     */
    @Bean
    public Queue seckillOrderQueue() {
        return new Queue("queue.kun", true, false, false);
    }

    /**
     * 绑定秒杀队列
     */
    @Bean
    public Binding bindSeckillQueue() {
        return BindingBuilder
                .bind(seckillOrderQueue())
                .to(seckillExchange())
                .with("queue.kun");
    }

    // ========== 订单超时取消配置 ==========

    /**
     * 订单取消交换机（用于接收死信）
     */
    @Bean
    public DirectExchange orderCancelExchange() {
        return new DirectExchange("order.cancel.exchange", true, false);
    }

    /**
     * 订单取消业务队列（消费者监听这个队列）
     * 消费者会检查订单状态，决定是否取消订单
     */
    @Bean
    public Queue orderCancelQueue() {
        return new Queue("order.cancel.queue", true, false, false);
    }

    /**
     * 绑定订单取消队列到交换机
     */
    @Bean
    public Binding bindOrderCancelQueue() {
        return BindingBuilder
                .bind(orderCancelQueue())
                .to(orderCancelExchange())
                .with("order.cancel");
    }

    /**
     * 订单延时队列（等待30分钟）
     * ⚠️ 重点：没有消费者监听这个队列，只用来延时
     * 消息在这里等待30分钟后过期，自动进入死信队列
     */
    @Bean
    public Queue orderDelayQueue() {
        Map<String, Object> args = new HashMap<>();

        // ✅ 设置消息过期时间：1分钟
        args.put("x-message-ttl", 60000);  // 1分钟 = 60 * 1000 毫秒

        // ✅ 设置死信交换机：消息过期后发送到这个交换机
        args.put("x-dead-letter-exchange", "order.cancel.exchange");

        // ✅ 设置死信路由键：过期后使用这个路由键
        args.put("x-dead-letter-routing-key", "order.cancel");

        /*
            消息过期后会进入死信交换机"order.cancel.exchange"，然后死信交换机会根据路由键"order.cancel"发送到"order.cancel.queue"
            这个队列，这个队列会被消费者监听，消费者会检查订单状态，决定是否取消订单
         */
        return new Queue("delay.order.cancel.queue", true, false, false, args);
    }

    /**
     * 延时队列的直连交换机（发送消息时用）
     */
    @Bean
    public DirectExchange orderDelayExchange() {
        return new DirectExchange("order.delay.exchange", true, false);
    }

    /**
     * 绑定延时队列到延时交换机
     */
    @Bean
    public Binding bindOrderDelayQueue() {
        return BindingBuilder
                .bind(orderDelayQueue())
                .to(orderDelayExchange())
                .with("order.delay");
    }
}
